/*
 * Copyright 2009 Cedric Priscal
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cyItem.sesrialPort;

import android.os.SystemClock;
import android.serialport.api.SerialPortDataReceived;
import android.util.Log;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;

public class SerialPort {
    private static final String TAG = "SerialPort";
    private ReadThread mReadThread;
    private boolean isReving = false;
    private FileDescriptor mFd;
    public FileInputStream mFileInputStream;
    public FileOutputStream mFileOutputStream;
    private static byte[] buffer = null;
    private String device = "";
    private int baudrate = 115200;
    static byte[] recByteArray = new byte[4096];
    static int recSize = 0;
    static boolean getDataFlg = false;
    static SerialPortDataReceived serialportDataReceived = null;
    public static boolean beginScanning = false;
    public boolean isOpen = false;
    int tempSize = 0;
    private final ReentrantLock lock = new ReentrantLock();


    public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {

        /* Check access permission */
        if (!device.canRead() || !device.canWrite()) {
            try {
                /* Missing read/write permission, trying to chmod the file */
                Process su;
                su = Runtime.getRuntime().exec("/system/bin/su");
                String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
                        + "exit\n";
                su.getOutputStream().write(cmd.getBytes());
                if ((su.waitFor() != 0) || !device.canRead()
                        || !device.canWrite()) {
                    throw new SecurityException();
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new SecurityException();
            }
        }

        mFd = open(device.getAbsolutePath(), baudrate, flags);
        if (mFd == null) {
            Log.e(TAG, "native open returns null");
            throw new IOException();
        }
        mFileInputStream = new FileInputStream(mFd);
        mFileOutputStream = new FileOutputStream(mFd);
    }


    public void setOnSerialPortDataReceived(SerialPortDataReceived _serialPortDataReceived) {
        serialportDataReceived = _serialPortDataReceived;
    }

    private boolean RootCommand(String command) {
        Process process = null;
        DataOutputStream os = null;

        try {
            process = Runtime.getRuntime().exec("su");
            os = new DataOutputStream(process.getOutputStream());
            os.writeBytes(command + "\n");
            os.writeBytes("exit\n");
            os.flush();
            process.waitFor();
            return true;
        } catch (Exception var13) {
            Log.d("*** DEBUG ***", "ROOT REE" + var13.getMessage());
        } finally {
            try {
                if (os != null) {
                    os.close();
                }

                process.destroy();
            } catch (Exception var12) {
                // just ignore
            }

        }

        return false;
    }

    public boolean open(String device, int baudRate) {
        this.device = device;
        this.baudrate = baudRate;
        if (!this.isOpen) {
            if ("".equals(device)) {
                Log.e(TAG, "serialPort device is null");
                return false;
            } else {
                this.mFd = open(device, baudRate, 0);
                if (this.mFd == null) {
                    Log.e(TAG, "can not open device " + device);
                    return false;
                } else {
                    this.mFileInputStream = new FileInputStream(this.mFd);
                    this.mFileOutputStream = new FileOutputStream(this.mFd);
                    this.mReadThread = new ReadThread();
                    this.isReving = true;
                    this.mReadThread.start();
                    this.isOpen = true;
                    return true;
                }
            }
        } else {
            return true;
        }
    }

    public boolean open() {
        if ("".equals(device)) {
            Log.e(TAG, "serialPort device is null");
            return false;
        } else {
            if (!this.isOpen) {
                this.mFd = open(this.device, this.baudrate, 0);
                if (this.mFd == null) {
                    Log.e(TAG, "can not open device " + this.device);
                    return false;
                }

                this.mFileInputStream = new FileInputStream(this.mFd);
                this.mFileOutputStream = new FileOutputStream(this.mFd);
                this.mReadThread = new ReadThread();
                this.isReving = true;
                this.mReadThread.start();
                this.isOpen = true;
            }

            return true;
        }
    }

    public boolean openNoRead(String device, int baudRate) {
        this.device = device;
        this.baudrate = baudRate;
        if (!this.isOpen) {
            if (device.equals("")) {
                Log.e(TAG, "serialPort device is null");
                return false;
            } else {
                this.mFd = open(device, baudRate, 0);
                if (this.mFd == null) {
                    Log.e(TAG, "can not open device " + device);
                    return false;
                } else {
                    this.mFileInputStream = new FileInputStream(this.mFd);
                    this.mFileOutputStream = new FileOutputStream(this.mFd);
                    this.isOpen = true;
                    return true;
                }
            }
        } else {
            return true;
        }
    }

    public void closePort() {
        this.isReving = false;
        if (this.mReadThread != null) {
            this.mReadThread.interrupt();
            this.mReadThread = null;
        }

        if (this.isOpen) {
            this.close();
        }

        if (this.mFd != null) {
            try {
                this.mFd = null;
                this.mFileInputStream = null;
                this.mFileOutputStream = null;
                this.isOpen = false;
            } catch (Exception var2) {
                Log.e(TAG, var2.getMessage());
            }
        }
    }

    public boolean WriteNoSleep(byte[] buffer) {
        if (this.mFileOutputStream == null) {
            return false;
        } else {
            try {
                this.mFileOutputStream.write(buffer);
                if (buffer.length < 1000) {
                    Log.i(TAG, "SerialPort WriteNoSleep:" + new String(buffer, 0, buffer.length));
                }
            } catch (IOException var3) {
                var3.printStackTrace();
            }

            return true;
        }
    }

    public byte[] WriteGetData(byte[] buffer) {
        getDataFlg = true;
        byte[] recData = null;
        int tempSize = 0;
        int nullNo = 0;

        try {
            if (this.mFileOutputStream != null) {
                this.mFileOutputStream.write(buffer);
            }
            Log.e(TAG, "SerialPort write get data write buffer " + Arrays.toString(buffer));
            for (int e = 0; e < 500; ++e) {
                Log.e(TAG, "SerialPort write get data write e " + e);
                Log.e(TAG, "SerialPort write get data write tempSize " + tempSize);
                if (tempSize != recSize) {
                    tempSize = recSize;
                } else if (tempSize == recSize && nullNo++ > 100) {
                    break;
                }
                SystemClock.sleep(1L);
            }
            recData = new byte[recSize];
            System.arraycopy(recByteArray, 0, recData, 0, recData.length);
            recSize = 0;
            recByteArray = new byte[4096];
        } catch (IOException e) {
            Log.e(TAG, e.getMessage());
            e.printStackTrace();
        }

        getDataFlg = false;
        return recData;
    }

    public boolean WriteByteArray(byte[] buffer) {
        try {
            if (this.mFileOutputStream == null) {
                return false;
            } else {
                this.mFileOutputStream.write(buffer);
                return true;
            }
        } catch (IOException e) {
            Log.e(TAG, "WriteByteArray error :" + e.getMessage());
            e.printStackTrace();
            return true;
        }
    }

    public boolean WriteString(String str) {
        byte[] buffer = str.getBytes();
        return this.WriteByteArray(buffer);
    }

    protected void onDataReceived(byte[] bufferRec, int size) {
        if (serialportDataReceived != null) {
            if (serialportDataReceived != null) {
                serialportDataReceived.onDataReceivedListener(bufferRec, size);
            } else {
                Log.e(TAG, "serialPortDataReceived is null");
            }
        }

    }

    protected class ReadThread extends Thread {
        private static final String TAG = "ReadThread";

        protected ReadThread() {
        }

        public void run() {
            for (; SerialPort.this.isReving; SystemClock.sleep(1L)) {
                try {
                    if (SerialPort.this.mFileInputStream != null && SerialPort.this.mFileInputStream.available() > 0) {
                        byte[] e = new byte[512];
                        int size = SerialPort.this.mFileInputStream.read(e);
                        if (size > 0) {
                            if (SerialPort.getDataFlg && SerialPort.recByteArray != null) {
                                System.arraycopy(e, 0, SerialPort.recByteArray, SerialPort.recSize, size);
                                Log.e(TAG, "SerialPort ReadThread:" + SerialPort.recSize);
                                SerialPort.recSize += size;
                            }
                            SerialPort.buffer = new byte[size];
                            System.arraycopy(e, 0, SerialPort.buffer, 0, SerialPort.buffer.length);
                            SerialPort.this.onDataReceived(e, size);
                        }
                    }
                } catch (IOException e) {
                    Log.e(TAG, "SerialPort error : " + e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }

    // Getters and setters
    public InputStream getInputStream() {
        return mFileInputStream;
    }

    public OutputStream getOutputStream() {
        return mFileOutputStream;
    }

    // JNI
    private native static FileDescriptor open(String path, int baudrate, int flags);

    public native void close();

    static {
        System.loadLibrary("serial_port");
    }
}
